<!DOCTYPE html>
<html>

<head>
  <title>Quarkus - Using JWT RBAC</title>
  <script id="adobe_dtm" src="https://www.redhat.com/dtm.js" type="text/javascript"></script>
  <script src="/assets/javascript/highlight.pack.js" type="text/javascript"></script>
  <META HTTP-EQUIV='Content-Security-Policy' CONTENT="default-src 'none'; script-src 'self' 'unsafe-eval' 'sha256-ANpuoVzuSex6VhqpYgsG25OHWVA1I+F6aGU04LoI+5s=' 'sha256-ipy9P/3rZZW06mTLAR0EnXvxSNcnfSDPLDuh3kzbB1w=' js.bizographics.com https://www.redhat.com assets.adobedtm.com jsonip.com https://ajax.googleapis.com https://www.googletagmanager.com https://www.google-analytics.com https://use.fontawesome.com; style-src 'self' https://fonts.googleapis.com https://use.fontawesome.com; img-src 'self' *; media-src 'self' ; frame-src https://www.googletagmanager.com https://www.youtube.com; frame-ancestors 'none'; base-uri 'none'; object-src 'none'; form-action 'none'; font-src 'self' https://use.fontawesome.com https://fonts.gstatic.com;">
  <META HTTP-EQUIV='X-Frame-Options' CONTENT="DENY">
  <META HTTP-EQUIV='X-XSS-Protection' CONTENT="1; mode=block">
  <META HTTP-EQUIV='X-Content-Type-Options' CONTENT="nosniff">
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="description" content="Quarkus: Supersonic Subatomic Java">
  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:site" content="@QuarkusIO"> 
  <meta name="twitter:creator" content="@QuarkusIO">
  <meta property="og:url" content="https://quarkus.io/guides/security-jwt" />
  <meta property="og:title" content="Quarkus - Using JWT RBAC" />
  <meta property="og:description" content="Quarkus: Supersonic Subatomic Java" />
  <meta property="og:image" content="/assets/images/quarkus_card.png" />
  <link rel="canonical" href="https://quarkus.io/guides/security-jwt">
  <link rel="shortcut icon" type="image/png" href="/favicon.ico" >
  <link rel="stylesheet" href="https://quarkus.io/guides/stylesheet/config.css" />
  <link rel="stylesheet" href="/assets/css/main.css" />
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/all.css" integrity="sha384-lKuwvrZot6UHsBSfcMvOkWwlCMgc0TaWr+30HWe3a4ltaBwTZhyTEggF5tJv8tbt" crossorigin="anonymous">
  <link rel="alternate" type="application/rss+xml"  href="https://quarkus.io/feed.xml" title="Quarkus">
  <script src="https://quarkus.io/assets/javascript/goan.js" type="text/javascript"></script>
  <script src="https://quarkus.io/assets/javascript/hl.js" type="text/javascript"></script>
</head>

<body class="guides">
  <!-- Google Tag Manager (noscript) -->
  <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-NJWS5L"
  height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
  <!-- End Google Tag Manager (noscript) -->

  <div class="nav-wrapper">
  <div class="grid-wrapper">
    <div class="width-12-12">
      <input type="checkbox" id="checkbox" />
      <nav id="main-nav" class="main-nav">
  <div class="container">
    <div class="logo-wrapper">
      
        <a href="/"><img src="/assets/images/quarkus_logo_horizontal_rgb_600px_reverse.png" class="project-logo" title="Quarkus"></a>
      
    </div>
    <label class="nav-toggle" for="checkbox">
      <i class="fa fa-bars"></i>
    </label>
    <div id="menu" class="menu">
      <span>
        <a href="/get-started/" class="">Get Started</a>
      </span>
      <span>
        <a href="/guides/" class="active">Guides</a>
      </span>
      <span>
        <a href="/community/" class="">Community</a>
      </span>
      <span>
        <a href="/support/" class="">Support</a>
      </span>
      <span>
        <a href="/blog/" class="">Blog</a>
      </span>
      <span>
        <a href="https://code.quarkus.io" class="button-cta secondary white">Start Coding</a>
      </span>
    </div>
  </div>
      </nav>
    </div>
  </div>
</div>

  <div class="content">
    <div class="guide">
  <div class="width-12-12">
    <h1 class="text-caps">Quarkus - Using JWT RBAC</h1>
    <div class="hide-mobile toc"><ul class="sectlevel1">
<li><a href="#solution">Solution</a>
<ul class="sectlevel2">
<li><a href="#creating-the-maven-project">Creating the Maven project</a></li>
<li><a href="#examine-the-jax-rs-resource">Examine the JAX-RS resource</a></li>
<li><a href="#run-the-application">Run the application</a></li>
<li><a href="#configuring-the-smallrye-jwt-extension-security-information">Configuring the SmallRye JWT Extension Security Information</a></li>
<li><a href="#adding-a-public-key">Adding a Public Key</a></li>
<li><a href="#generating-a-jwt">Generating a JWT</a></li>
</ul>
</li>
<li><a href="#finally-secured-access-to-securedroles-allowed">Finally, Secured Access to /secured/roles-allowed</a>
<ul class="sectlevel2">
<li><a href="#using-the-jsonwebtoken-and-claim-injection">Using the JsonWebToken and Claim Injection</a></li>
<li><a href="#package-and-run-the-application">Package and run the application</a></li>
<li><a href="#explore-the-solution">Explore the Solution</a></li>
</ul>
</li>
<li><a href="#configuration-reference">Configuration Reference</a>
<ul class="sectlevel2">
<li><a href="#quarkus-configuration">Quarkus configuration</a></li>
<li><a href="#microprofile-jwt-configuration">MicroProfile JWT configuration</a></li>
<li><a href="#supported-public-key-formats">Supported Public Key Formats</a></li>
<li><a href="#additional-smallrye-jwt-configuration">Additional SmallRye JWT configuration</a></li>
</ul>
</li>
<li><a href="#create-jsonwebtoken-with-jwtparser">Create JsonWebToken with JWTParser</a></li>
<li><a href="#token-decryption">Token Decryption</a></li>
<li><a href="#how-to-check-the-errors-in-the-logs">How to check the errors in the logs</a></li>
<li><a href="#generate-jwt-tokens">Generate JWT tokens with SmallRye JWT</a>
<ul class="sectlevel2">
<li><a href="#create-jwtclaimsbuilder-and-set-the-claims">Create JwtClaimsBuilder and set the claims</a></li>
<li><a href="#sign-the-claims">Sign the claims</a></li>
<li><a href="#encrypt-the-claims">Encrypt the claims</a></li>
<li><a href="#sign-the-claims-and-encrypt-the-nested-jwt-token">Sign the claims and encrypt the nested JWT token</a></li>
<li><a href="#fast-jwt-generation">Fast JWT Generation</a></li>
<li><a href="#smallrye-jwt-builder-configuration">SmallRye JWT Builder configuration</a></li>
</ul>
</li>
<li><a href="#references">References</a></li>
</ul></div>
    <div>
      <div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>This guide explains how your Quarkus application can utilize MicroProfile JWT (MP JWT) to verify <a href="https://tools.ietf.org/html/rfc7519">JSON Web Token</a>s, represent them as MP JWT <code>org.eclipse.microprofile.jwt.JsonWebToken</code> and provide secured access to the Quarkus HTTP endpoints using Bearer Token Authorization and <a href="https://en.wikipedia.org/wiki/Role-based_access_control">Role-Based Access Control</a>.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Quarkus OpenId Connect extension also supports Bearer Token Authorization and uses <code>smallrye-jwt</code> to represent the bearer tokens as <code>JsonWebToken</code>, please read the <a href="security-openid-connect">Using OpenID Connect to Protect Service Applications</a> guide for more information.
OpenId Connect extension has to be used if the Quarkus application needs to authenticate the users using OIDC Authorization Code Flow, please read <a href="security-openid-connect-web-authentication">Using OpenID Connect to Protect Web Applications</a> guide for more information.
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="solution"><a class="anchor" href="#solution"></a>Solution</h2>
<div class="sectionbody">
<div class="paragraph">
<p>We recommend that you follow the instructions in the next sections and create the application step by step.
However, you can skip right to the completed example.</p>
</div>
<div class="paragraph">
<p>Clone the Git repository: <code>git clone <a href="https://github.com/quarkusio/quarkus-quickstarts.git" class="bare">https://github.com/quarkusio/quarkus-quickstarts.git</a></code>, or download an <a href="https://github.com/quarkusio/quarkus-quickstarts/archive/master.zip">archive</a>.</p>
</div>
<div class="paragraph">
<p>The solution is located in the <code>security-jwt-quickstart</code> <a href="https://github.com/quarkusio/quarkus-quickstarts/tree/master/security-jwt-quickstart">directory</a>.</p>
</div>
<div class="sect2">
<h3 id="creating-the-maven-project"><a class="anchor" href="#creating-the-maven-project"></a>Creating the Maven project</h3>
<div class="paragraph">
<p>First, we need a new project. Create a new project with the following command:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code data-lang="shell" class="language-shell hljs">mvn io.quarkus:quarkus-maven-plugin:1.7.0.Final:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=security-jwt-quickstart \
    -DclassName="org.acme.security.jwt.TokenSecuredResource" \
    -Dpath="/secured" \
    -Dextensions="resteasy-jsonb, jwt"
cd security-jwt-quickstart</code></pre>
</div>
</div>
<div class="paragraph">
<p>This command generates the Maven project with a REST endpoint and imports the <code>smallrye-jwt</code> extension, which includes the MicroProfile JWT RBAC support.</p>
</div>
<div class="paragraph">
<p>If you already have your Quarkus project configured, you can add the <code>smallrye-jwt</code> extension
to your project by running the following command in your project base directory:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code data-lang="bash" class="language-bash hljs">./mvnw quarkus:add-extension -Dextensions="smallrye-jwt"</code></pre>
</div>
</div>
<div class="paragraph">
<p>This will add the following to your <code>pom.xml</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code data-lang="xml" class="language-xml hljs">&lt;dependency&gt;
    &lt;groupId&gt;io.quarkus&lt;/groupId&gt;
    &lt;artifactId&gt;quarkus-smallrye-jwt&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="examine-the-jax-rs-resource"><a class="anchor" href="#examine-the-jax-rs-resource"></a>Examine the JAX-RS resource</h3>
<div class="paragraph">
<p>Open the <code>src/main/java/org/acme/security/jwt/TokenSecuredResource.java</code> file and see the following content:</p>
</div>
<div class="listingblock">
<div class="title">Basic REST Endpoint</div>
<div class="content">
<pre class="highlightjs highlight"><code data-lang="java" class="language-java hljs">package org.acme.security.jwt;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/secured")
public class TokenSecuredResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "hello";
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This is a basic REST endpoint that does not have any of the SmallRye JWT specific features, so let&#8217;s add some.</p>
</div>
<div class="listingblock">
<div class="title">REST Endpoint V1</div>
<div class="content">
<pre class="highlightjs highlight"><code data-lang="java" class="language-java hljs">package org.acme.security.jwt;

import java.security.Principal;

import javax.annotation.security.PermitAll;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.SecurityContext;

import org.eclipse.microprofile.jwt.JsonWebToken;

@Path("/secured")
@RequestScoped <i class="conum" data-value="1"></i><b>(1)</b>
public class TokenSecuredResource {

    @Inject
    JsonWebToken jwt; <i class="conum" data-value="2"></i><b>(2)</b>

    @GET()
    @Path("permit-all")
    @PermitAll <i class="conum" data-value="3"></i><b>(3)</b>
    @Produces(MediaType.TEXT_PLAIN)
    public String hello(@Context SecurityContext ctx) {
        return getResponseString(ctx); <i class="conum" data-value="4"></i><b>(4)</b>
    }

    private String getResponseString(SecurityContext ctx) {
        String name;
        if (ctx.getUserPrincipal() == null) { <i class="conum" data-value="5"></i><b>(5)</b>
            name = "anonymous";
        } else if (!ctx.getUserPrincipal().getName().equals(jwt.getName())) { <i class="conum" data-value="6"></i><b>(6)</b>
            throw new InternalServerErrorException("Principal and JsonWebToken names do not match");
        } else {
            name = ctx.getUserPrincipal().getName(); <i class="conum" data-value="7"></i><b>(7)</b>
        }
        return String.format("hello + %s,"
            + " isHttps: %s,"
            + " authScheme: %s,"
            + " hasJWT: %s",
            name, ctx.isSecure(), ctx.getAuthenticationScheme(), hasJwt()); <i class="conum" data-value="8"></i><b>(8)</b>
    }

    private boolean hasJwt() {
	return jwt.getClaimNames() != null;
    }
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Add a <code>RequestScoped</code> as Quarkus uses a default scoping of <code>ApplicationScoped</code> and this
will produce undesirable behavior since JWT claims are naturally request scoped.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Here we inject the JsonWebToken interface, an extension of the java.security.Principal interface that provides access to the claims associated with the current authenticated token.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>@PermitAll is a JSR 250 common security annotation that indicates that the given endpoint is accessible by any caller, authenticated or not.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Here we inject the JAX-RS SecurityContext to inspect the security state of the call and use a <code>getResponseString()</code> function to populate a response string.</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Here we check if the call is insecured by checking the request user/caller <code>Principal</code> against null.</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>Here we check that the Principal and JsonWebToken have the same name since JsonWebToken does represent the current Principal.</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>Here we get the Principal name.</td>
</tr>
<tr>
<td><i class="conum" data-value="8"></i><b>8</b></td>
<td>The reply we build up makes use of the caller name, the <code>isSecure()</code> and <code>getAuthenticationScheme()</code> states of the request <code>SecurityContext</code>, and whether a non-null <code>JsonWebToken</code> was injected.</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="run-the-application"><a class="anchor" href="#run-the-application"></a>Run the application</h3>
<div class="paragraph">
<p>Now we are ready to run our application. Use:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code data-lang="shell" class="language-shell hljs">./mvnw compile quarkus:dev</code></pre>
</div>
</div>
<div class="paragraph">
<p>and you should see output similar to:</p>
</div>
<div class="listingblock">
<div class="title">quarkus:dev Output</div>
<div class="content">
<pre class="highlightjs highlight"><code data-lang="shell" class="language-shell hljs">$ ./mvnw compile quarkus:dev
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------------&lt; org.acme:security-jwt-quickstart &gt;-----------------------
[INFO] Building security-jwt-quickstart 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
...
Listening for transport dt_socket at address: 5005
2020-07-15 16:09:50,883 INFO  [io.quarkus] (Quarkus Main Thread) security-jwt-quickstart 1.0-SNAPSHOT on JVM (powered by Quarkus 999-SNAPSHOT) started in 1.073s. Listening on: http://0.0.0.0:8080
2020-07-15 16:09:50,885 INFO  [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2020-07-15 16:09:50,885 INFO  [io.quarkus] (Quarkus Main Thread) Installed features: [cdi, mutiny, resteasy, resteasy-jsonb, security, smallrye-context-propagation, smallrye-jwt, vertx, vertx-web]</code></pre>
</div>
</div>
<div class="paragraph">
<p>Now that the REST endpoint is running, we can access it using a command line tool like curl:</p>
</div>
<div class="listingblock">
<div class="title">curl command for /secured/permit-all</div>
<div class="content">
<pre class="highlightjs highlight"><code data-lang="shell" class="language-shell hljs">$ curl http://127.0.0.1:8080/secured/permit-all; echo
hello + anonymous, isHttps: false, authScheme: null, hasJWT: false</code></pre>
</div>
</div>
<div class="paragraph">
<p>We have not provided any JWT in our request, so we would not expect that there is any security state seen by the endpoint, and
the response is consistent with that:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>user name is anonymous</p>
</li>
<li>
<p>isHttps is false as https is not used</p>
</li>
<li>
<p>authScheme is null</p>
</li>
<li>
<p>hasJWT is false</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Use Ctrl-C to stop the Quarkus server.</p>
</div>
<div class="paragraph">
<p>So now let&#8217;s actually secure something. Take a look at the new endpoint method <code>helloRolesAllowed</code> in the following:</p>
</div>
<div class="listingblock">
<div class="title">REST Endpoint V2</div>
<div class="content">
<pre class="highlightjs highlight"><code data-lang="java" class="language-java hljs">package org.acme.security.jwt;

import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.SecurityContext;

import org.eclipse.microprofile.jwt.JsonWebToken;

@Path("/secured")
@RequestScoped
public class TokenSecuredResource {

    @Inject
    JsonWebToken jwt; <i class="conum" data-value="1"></i><b>(1)</b>

    @GET
    @Path("permit-all")
    @PermitAll
    @Produces(MediaType.TEXT_PLAIN)
    public String hello(@Context SecurityContext ctx) {
        return getResponseString(ctx);
    }

    @GET
    @Path("roles-allowed") <i class="conum" data-value="2"></i><b>(2)</b>
    @RolesAllowed({ "User", "Admin" }) <i class="conum" data-value="3"></i><b>(3)</b>
    @Produces(MediaType.TEXT_PLAIN)
    public String helloRolesAllowed(@Context SecurityContext ctx) {
        return getResponseString(ctx) + ", birthdate: " + jwt.getClaim("birthdate").toString(); <i class="conum" data-value="4"></i><b>(4)</b>
    }

    private String getResponseString(SecurityContext ctx) {
        String name;
        if (ctx.getUserPrincipal() == null) {
            name = "anonymous";
        } else if (!ctx.getUserPrincipal().getName().equals(jwt.getName())) {
            throw new InternalServerErrorException("Principal and JsonWebToken names do not match");
        } else {
            name = ctx.getUserPrincipal().getName();
        }
        return String.format("hello + %s,"
            + " isHttps: %s,"
            + " authScheme: %s,"
            + " hasJWT: %s",
            name, ctx.isSecure(), ctx.getAuthenticationScheme(), hasJwt());
    }

    private boolean hasJwt() {
        return jwt.getClaimNames() != null;
    }
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Here we inject <code>JsonWebToken</code></td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>This new endpoint will be located at /secured/roles-allowed</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>@RolesAllowed is a JSR 250 common security annotation that indicates that the given endpoint is accessible by a caller if
they have either a "User" or "Admin" role assigned.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Here we build the reply the same way as in the <code>hello</code> method but also add a value of the JWT <code>birthdate</code> claim by directly calling the injected <code>JsonWebToken</code>.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>After you make this addition to your <code>TokenSecuredResource</code>, rerun the <code>./mvnw compile quarkus:dev</code> command, and then try <code>curl -v <a href="http://127.0.0.1:8080/secured/roles-allowed" class="bare">http://127.0.0.1:8080/secured/roles-allowed</a>; echo</code> to attempt to access the new endpoint. Your output should be:</p>
</div>
<div class="listingblock">
<div class="title">curl command for /secured/roles-allowed</div>
<div class="content">
<pre class="highlightjs highlight"><code data-lang="shell" class="language-shell hljs">$ curl -v http://127.0.0.1:8080/secured/roles-allowed; echo
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
&gt; GET /secured/roles-allowed HTTP/1.1
&gt; Host: 127.0.0.1:8080
&gt; User-Agent: curl/7.54.0
&gt; Accept: */*
&gt;
&lt; HTTP/1.1 401 Unauthorized
&lt; Connection: keep-alive
&lt; Content-Type: text/html;charset=UTF-8
&lt; Content-Length: 14
&lt; Date: Sun, 03 Mar 2019 16:32:34 GMT
&lt;
* Connection #0 to host 127.0.0.1 left intact
Not authorized</code></pre>
</div>
</div>
<div class="paragraph">
<p>Excellent, we have not provided any JWT in the request, so we should not be able to access the endpoint, and we were not. Instead we received an HTTP 401 Unauthorized error. We need to obtain and pass in a valid JWT to access that endpoint. There are two steps to this, 1) configuring our SmallRye JWT extension with information on how to validate a JWT, and 2) generating a matching JWT with the appropriate claims.</p>
</div>
</div>
<div class="sect2">
<h3 id="configuring-the-smallrye-jwt-extension-security-information"><a class="anchor" href="#configuring-the-smallrye-jwt-extension-security-information"></a>Configuring the SmallRye JWT Extension Security Information</h3>
<div class="paragraph">
<p>Create a <code>security-jwt-quickstart/src/main/resources/application.properties</code> with the following content:</p>
</div>
<div class="listingblock">
<div class="title">application.properties for TokenSecuredResource</div>
<div class="content">
<pre class="highlightjs highlight"><code data-lang="properties" class="language-properties hljs">mp.jwt.verify.publickey.location=META-INF/resources/publicKey.pem <i class="conum" data-value="1"></i><b>(1)</b>
mp.jwt.verify.issuer=https://quarkus.io/using-jwt-rbac <i class="conum" data-value="2"></i><b>(2)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>We are setting public key location to point to a classpath publicKey.pem resource location. We will add this key in part B, <a href="#adding-a-public-key">Adding a Public Key</a>.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>We are setting the issuer to the URL string <code><a href="https://quarkus.io/using-jwt-rbac" class="bare">https://quarkus.io/using-jwt-rbac</a></code>.</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="adding-a-public-key"><a class="anchor" href="#adding-a-public-key"></a>Adding a Public Key</h3>
<div class="paragraph">
<p>The <a href="https://tools.ietf.org/html/rfc7519">JWT specification</a> defines various levels of security of JWTs that one can use.
The MicroProfile JWT RBAC specification requires that JWTs that are signed with the RSA-256 signature algorithm. This in
turn requires a RSA public key pair. On the REST endpoint server side, you need to configure the location of the RSA public
key to use to verify the JWT sent along with requests. The <code>mp.jwt.verify.publickey.location=publicKey.pem</code> setting configured
previously expects that the public key is available on the classpath as <code>publicKey.pem</code>. To accomplish this, copy the following
content to a <code>security-jwt-quickstart/src/main/resources/META-INF/resources/publicKey.pem</code> file.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Adding <code>publicKey.pem</code> to <code>resources/META-INF/resources</code> ensures that it is available in the native image without having to provide a GraalVM resource file.
</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="title">RSA Public Key PEM Content</div>
<div class="content">
<pre class="highlightjs highlight"><code data-lang="text" class="language-text hljs">-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlivFI8qB4D0y2jy0CfEq
Fyy46R0o7S8TKpsx5xbHKoU1VWg6QkQm+ntyIv1p4kE1sPEQO73+HY8+Bzs75XwR
TYL1BmR1w8J5hmjVWjc6R2BTBGAYRPFRhor3kpM6ni2SPmNNhurEAHw7TaqszP5e
UF/F9+KEBWkwVta+PZ37bwqSE4sCb1soZFrVz/UT/LF4tYpuVYt3YbqToZ3pZOZ9
AX2o1GCG3xwOjkc4x0W7ezbQZdC9iftPxVHR8irOijJRRjcPDtA6vPKpzLl6CyYn
sIYPd99ltwxTHjr3npfv/3Lw50bAkbT4HeLFxTx4flEoZLKO/g0bAoV2uqBhkA9x
nQIDAQAB
-----END PUBLIC KEY-----</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="generating-a-jwt"><a class="anchor" href="#generating-a-jwt"></a>Generating a JWT</h3>
<div class="paragraph">
<p>Often one obtains a JWT from an identity manager like <a href="https://www.keycloak.org/">Keycloak</a>, but for this quickstart we will generate our own using the JWT generation API provided by <code>smallrye-jwt</code> (see <a href="#generate-jwt-tokens">Generate JWT tokens with SmallRye JWT</a> for more information).</p>
</div>
<div class="paragraph">
<p>Take the code from the following listing and place into <code>security-jwt-quickstart/src/main/java/org/acme/security/jwt/GenerateToken.java</code>:</p>
</div>
<div class="listingblock">
<div class="title">GenerateToken main Driver Class</div>
<div class="content">
<pre class="highlightjs highlight"><code data-lang="java" class="language-java hljs">package org.acme.security.jwt;

import java.util.Arrays;
import java.util.HashSet;

import org.eclipse.microprofile.jwt.Claims;

import io.smallrye.jwt.build.Jwt;

public class GenerateToken {
    /**
     * Generate JWT token
     */
    public static void main(String[] args) {
        String token =
           Jwt.issuer("https://quarkus.io/using-jwt-rbac") <i class="conum" data-value="1"></i><b>(1)</b>
             .upn("jdoe@quarkus.io") <i class="conum" data-value="2"></i><b>(2)</b>
             .groups(new HashSet&lt;&gt;(Arrays.asList("User", "Admin"))) <i class="conum" data-value="3"></i><b>(3)</b>
             .claim(Claims.birthdate.name(), "2001-07-13") <i class="conum" data-value="4"></i><b>(4)</b>
           .sign();
        System.out.println(token);
    }
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The <code>iss</code> claim is the issuer of the JWT. This needs to match the server side <code>mp.jwt.verify.issuer</code>.
in order for the token to be accepted as valid.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>The <code>upn</code> claim is defined by the MicroProfile JWT RBAC spec as preferred claim to use for the
<code>Principal</code> seen via the container security APIs.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>The <code>group</code> claim provides the groups and top-level roles associated with the JWT bearer.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>The <code>birthday</code> claim. It can be considered to be a sensitive claim so you may want to consider encrypting the claims, see <a href="#generate-jwt-tokens">Generate JWT tokens with SmallRye JWT</a>.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Note for this code to work we need the content of the RSA private key that corresponds to the public key we have in the TokenSecuredResource application. Take the following PEM content and place it into <code>security-jwt-quickstart/src/main/resources/META-INF/resources/privateKey.pem</code>:</p>
</div>
<div class="listingblock">
<div class="title">RSA Private Key PEM Content</div>
<div class="content">
<pre class="highlightjs highlight"><code data-lang="text" class="language-text hljs">-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCWK8UjyoHgPTLa
PLQJ8SoXLLjpHSjtLxMqmzHnFscqhTVVaDpCRCb6e3Ii/WniQTWw8RA7vf4djz4H
OzvlfBFNgvUGZHXDwnmGaNVaNzpHYFMEYBhE8VGGiveSkzqeLZI+Y02G6sQAfDtN
qqzM/l5QX8X34oQFaTBW1r49nftvCpITiwJvWyhkWtXP9RP8sXi1im5Vi3dhupOh
nelk5n0BfajUYIbfHA6ORzjHRbt7NtBl0L2J+0/FUdHyKs6KMlFGNw8O0Dq88qnM
uXoLJiewhg9332W3DFMeOveel+//cvDnRsCRtPgd4sXFPHh+UShkso7+DRsChXa6
oGGQD3GdAgMBAAECggEAAjfTSZwMHwvIXIDZB+yP+pemg4ryt84iMlbofclQV8hv
6TsI4UGwcbKxFOM5VSYxbNOisb80qasb929gixsyBjsQ8284bhPJR7r0q8h1C+jY
URA6S4pk8d/LmFakXwG9Tz6YPo3pJziuh48lzkFTk0xW2Dp4SLwtAptZY/+ZXyJ6
96QXDrZKSSM99Jh9s7a0ST66WoxSS0UC51ak+Keb0KJ1jz4bIJ2C3r4rYlSu4hHB
Y73GfkWORtQuyUDa9yDOem0/z0nr6pp+pBSXPLHADsqvZiIhxD/O0Xk5I6/zVHB3
zuoQqLERk0WvA8FXz2o8AYwcQRY2g30eX9kU4uDQAQKBgQDmf7KGImUGitsEPepF
KH5yLWYWqghHx6wfV+fdbBxoqn9WlwcQ7JbynIiVx8MX8/1lLCCe8v41ypu/eLtP
iY1ev2IKdrUStvYRSsFigRkuPHUo1ajsGHQd+ucTDf58mn7kRLW1JGMeGxo/t32B
m96Af6AiPWPEJuVfgGV0iwg+HQKBgQCmyPzL9M2rhYZn1AozRUguvlpmJHU2DpqS
34Q+7x2Ghf7MgBUhqE0t3FAOxEC7IYBwHmeYOvFR8ZkVRKNF4gbnF9RtLdz0DMEG
5qsMnvJUSQbNB1yVjUCnDAtElqiFRlQ/k0LgYkjKDY7LfciZl9uJRl0OSYeX/qG2
tRW09tOpgQKBgBSGkpM3RN/MRayfBtmZvYjVWh3yjkI2GbHA1jj1g6IebLB9SnfL
WbXJErCj1U+wvoPf5hfBc7m+jRgD3Eo86YXibQyZfY5pFIh9q7Ll5CQl5hj4zc4Y
b16sFR+xQ1Q9Pcd+BuBWmSz5JOE/qcF869dthgkGhnfVLt/OQzqZluZRAoGAXQ09
nT0TkmKIvlza5Af/YbTqEpq8mlBDhTYXPlWCD4+qvMWpBII1rSSBtftgcgca9XLB
MXmRMbqtQeRtg4u7dishZVh1MeP7vbHsNLppUQT9Ol6lFPsd2xUpJDc6BkFat62d
Xjr3iWNPC9E9nhPPdCNBv7reX7q81obpeXFMXgECgYEAmk2Qlus3OV0tfoNRqNpe
Mb0teduf2+h3xaI1XDIzPVtZF35ELY/RkAHlmWRT4PCdR0zXDidE67L6XdJyecSt
FdOUH8z5qUraVVebRFvJqf/oGsXc4+ex1ZKUTbY0wqY1y9E39yvB3MaTmZFuuqk8
f3cg+fr8aou7pr9SHhJlZCU=
-----END PRIVATE KEY-----</code></pre>
</div>
</div>
<div class="paragraph">
<p>We will use a <code>smallrye.jwt.sign.key-location</code> property to point to this private signing key.</p>
</div>
<div class="paragraph">
<p>Now we can generate a JWT to use with <code>TokenSecuredResource</code> endpoint. To do this, run the following command:</p>
</div>
<div class="listingblock">
<div class="title">Sample JWT Generation Output</div>
<div class="content">
<pre class="highlightjs highlight"><code data-lang="shell" class="language-shell hljs">$ mvn exec:java -Dexec.mainClass=org.acme.security.jwt.GenerateToken -Dexec.classpathScope=test -Dsmallrye.jwt.sign.key-location=privateKey.pem

eyJraWQiOiJcL3ByaXZhdGVLZXkucGVtIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJqZG9lLXVzaW5nLWp3dC1yYmFjIiwiYXVkIjoidXNpbmctand0LXJiYWMiLCJ1cG4iOiJqZG9lQHF1YXJrdXMuaW8iLCJiaXJ0aGRhdGUiOiIyMDAxLTA3LTEzIiwiYXV0aF90aW1lIjoxNTUxNjU5Njc2LCJpc3MiOiJodHRwczpcL1wvcXVhcmt1cy5pb1wvdXNpbmctand0LXJiYWMiLCJyb2xlTWFwcGluZ3MiOnsiZ3JvdXAyIjoiR3JvdXAyTWFwcGVkUm9sZSIsImdyb3VwMSI6Ikdyb3VwMU1hcHBlZFJvbGUifSwiZ3JvdXBzIjpbIkVjaG9lciIsIlRlc3RlciIsIlN1YnNjcmliZXIiLCJncm91cDIiXSwicHJlZmVycmVkX3VzZXJuYW1lIjoiamRvZSIsImV4cCI6MTU1MTY1OTk3NiwiaWF0IjoxNTUxNjU5Njc2LCJqdGkiOiJhLTEyMyJ9.O9tx_wNNS4qdpFhxeD1e7v4aBNWz1FCq0UV8qmXd7dW9xM4hA5TO-ZREk3ApMrL7_rnX8z81qGPIo_R8IfHDyNaI1SLD56gVX-NaOLS2OjfcbO3zOWJPKR_BoZkYACtMoqlWgIwIRC-wJKUJU025dHZiNL0FWO4PjwuCz8hpZYXIuRscfFhXKrDX1fh3jDhTsOEFfu67ACd85f3BdX9pe-ayKSVLh_RSbTbBPeyoYPE59FW7H5-i8IE-Gqu838Hz0i38ksEJFI25eR-AJ6_PSUD0_-TV3NjXhF3bFIeT4VSaIZcpibekoJg0cQm-4ApPEcPLdgTejYHA-mupb8hSwg</code></pre>
</div>
</div>
<div class="paragraph">
<p>The JWT string is the Base64 URL encoded string that has 3 parts separated by '.' characters.
First part - JWT headers, second part - JWT claims, third part - JWT signature.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="finally-secured-access-to-securedroles-allowed"><a class="anchor" href="#finally-secured-access-to-securedroles-allowed"></a>Finally, Secured Access to /secured/roles-allowed</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Now let&#8217;s use this to make a secured request to the /secured/roles-allowed endpoint. Make sure you have the Quarkus server running using the <code>./mvnw compile quarkus:dev</code> command, and then run the following command, making sure to use your version of the generated JWT from the previous step:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code data-lang="shell" class="language-shell hljs">curl -H "Authorization: Bearer eyJraWQiOiJcL3ByaXZhdGVLZXkucGVtIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJqZG9lLXVzaW5nLWp3dC1yYmFjIiwiYXVkIjoidXNpbmctand0LXJiYWMiLCJ1cG4iOiJqZG9lQHF1YXJrdXMuaW8iLCJiaXJ0aGRhdGUiOiIyMDAxLTA3LTEzIiwiYXV0aF90aW1lIjoxNTUxNjUyMDkxLCJpc3MiOiJodHRwczpcL1wvcXVhcmt1cy5pb1wvdXNpbmctand0LXJiYWMiLCJyb2xlTWFwcGluZ3MiOnsiZ3JvdXAyIjoiR3JvdXAyTWFwcGVkUm9sZSIsImdyb3VwMSI6Ikdyb3VwMU1hcHBlZFJvbGUifSwiZ3JvdXBzIjpbIkVjaG9lciIsIlRlc3RlciIsIlN1YnNjcmliZXIiLCJncm91cDIiXSwicHJlZmVycmVkX3VzZXJuYW1lIjoiamRvZSIsImV4cCI6MTU1MTY1MjM5MSwiaWF0IjoxNTUxNjUyMDkxLCJqdGkiOiJhLTEyMyJ9.aPA4Rlc4kw7n_OZZRRk25xZydJy_J_3BRR8ryYLyHTO1o68_aNWWQCgpnAuOW64svPhPnLYYnQzK-l2vHX34B64JySyBD4y_vRObGmdwH_SEufBAWZV7mkG3Y4mTKT3_4EWNu4VH92IhdnkGI4GJB6yHAEzlQI6EdSOa4Nq8Gp4uPGqHsUZTJrA3uIW0TbNshFBm47-oVM3ZUrBz57JKtr0e9jv0HjPQWyvbzx1HuxZd6eA8ow8xzvooKXFxoSFCMnxotd3wagvYQ9ysBa89bgzL-lhjWtusuMFDUVYwFqADE7oOSOD4Vtclgq8svznBQ-YpfTHfb9QEcofMlpyjNA" http://127.0.0.1:8080/secured/roles-allowed; echo</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">curl Command for /secured/roles-allowed With JWT</div>
<div class="content">
<pre class="highlightjs highlight"><code data-lang="shell" class="language-shell hljs">$ curl -H "Authorization: Bearer eyJraWQ..." http://127.0.0.1:8080/secured/roles-allowed; echo
hello + jdoe@quarkus.io, isHttps: false, authScheme: MP-JWT, hasJWT: true, birthdate: 2001-07-13</code></pre>
</div>
</div>
<div class="paragraph">
<p>Success! We now have:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>a non-anonymous caller name of <a href="mailto:jdoe@quarkus.io">jdoe@quarkus.io</a></p>
</li>
<li>
<p>an authentication scheme of Bearer</p>
</li>
<li>
<p>a non-null JsonWebToken</p>
</li>
<li>
<p>birthdate claim value</p>
</li>
</ul>
</div>
<div class="sect2">
<h3 id="using-the-jsonwebtoken-and-claim-injection"><a class="anchor" href="#using-the-jsonwebtoken-and-claim-injection"></a>Using the JsonWebToken and Claim Injection</h3>
<div class="paragraph">
<p>Now that we can generate a JWT to access our secured REST endpoints, let&#8217;s see what more we can do with the <code>JsonWebToken</code>
interface and the JWT claims. The <code>org.eclipse.microprofile.jwt.JsonWebToken</code> interface extends the <code>java.security.Principal</code>
interface, and is in fact the type of the object that is returned by the <code>javax.ws.rs.core.SecurityContext#getUserPrincipal()</code> call we
used previously. This means that code that does not use CDI but does have access to the REST container <code>SecurityContext</code> can get
hold of the caller <code>JsonWebToken</code> interface by casting the <code>SecurityContext#getUserPrincipal()</code>.</p>
</div>
<div class="paragraph">
<p>The <code>JsonWebToken</code> interface defines methods for accessing claims in the underlying JWT. It provides accessors for common
claims that are required by the MicroProfile JWT RBAC specification as well as arbitrary claims that may exist in the JWT.</p>
</div>
<div class="paragraph">
<p>All the JWT claims can also be injected. Let&#8217;s expand our <code>TokenSecuredResource</code> with another endpoint /secured/roles-allowed-admin which users the injected <code>birthdate</code> claim
(as opposed to getting it from <code>JsonWebToken</code>):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code data-lang="java" class="language-java hljs">package org.acme.security.jwt;

import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.SecurityContext;

import org.eclipse.microprofile.jwt.Claim;
import org.eclipse.microprofile.jwt.Claims;
import org.eclipse.microprofile.jwt.JsonWebToken;

@Path("/secured")
@RequestScoped
public class TokenSecuredResource {

    @Inject
    JsonWebToken jwt;
    @Inject
    @Claim(standard = Claims.birthdate)
    String birthdate; <i class="conum" data-value="1"></i><b>(1)</b>

    @GET
    @Path("permit-all")
    @PermitAll
    @Produces(MediaType.TEXT_PLAIN)
    public String hello(@Context SecurityContext ctx) {
        return getResponseString(ctx);
    }

    @GET
    @Path("roles-allowed")
    @RolesAllowed({ "User", "Admin" })
    @Produces(MediaType.TEXT_PLAIN)
    public String helloRolesAllowed(@Context SecurityContext ctx) {
        return getResponseString(ctx) + ", birthdate: " + jwt.getClaim("birthdate").toString();
    }

    @GET
    @Path("roles-allowed-admin")
    @RolesAllowed("Admin")
    @Produces(MediaType.TEXT_PLAIN)
    public String helloRolesAllowedAdmin(@Context SecurityContext ctx) {
        return getResponseString(ctx) + ", birthdate: " + birthdate; <i class="conum" data-value="2"></i><b>(2)</b>
    }

    private String getResponseString(SecurityContext ctx) {
        String name;
        if (ctx.getUserPrincipal() == null) {
            name = "anonymous";
        } else if (!ctx.getUserPrincipal().getName().equals(jwt.getName())) {
            throw new InternalServerErrorException("Principal and JsonWebToken names do not match");
        } else {
            name = ctx.getUserPrincipal().getName();
        }
        return String.format("hello + %s,"
            + " isHttps: %s,"
            + " authScheme: %s,"
            + " hasJWT: %s",
            name, ctx.isSecure(), ctx.getAuthenticationScheme(), hasJwt());
    }

    private boolean hasJwt() {
        return jwt.getClaimNames() != null;
    }
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Here we use the injected <code>birthday</code> claim.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Here we use the injected <code>birthday</code> claim to build the final reply.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Now generate the token again and run:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code data-lang="shell" class="language-shell hljs">curl -H "Authorization: Bearer eyJraWQiOiJcL3ByaXZhdGVLZXkucGVtIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJqZG9lLXVzaW5nLWp3dC1yYmFjIiwiYXVkIjoidXNpbmctand0LXJiYWMiLCJ1cG4iOiJqZG9lQHF1YXJrdXMuaW8iLCJiaXJ0aGRhdGUiOiIyMDAxLTA3LTEzIiwiYXV0aF90aW1lIjoxNTUxNjUyMDkxLCJpc3MiOiJodHRwczpcL1wvcXVhcmt1cy5pb1wvdXNpbmctand0LXJiYWMiLCJyb2xlTWFwcGluZ3MiOnsiZ3JvdXAyIjoiR3JvdXAyTWFwcGVkUm9sZSIsImdyb3VwMSI6Ikdyb3VwMU1hcHBlZFJvbGUifSwiZ3JvdXBzIjpbIkVjaG9lciIsIlRlc3RlciIsIlN1YnNjcmliZXIiLCJncm91cDIiXSwicHJlZmVycmVkX3VzZXJuYW1lIjoiamRvZSIsImV4cCI6MTU1MTY1MjM5MSwiaWF0IjoxNTUxNjUyMDkxLCJqdGkiOiJhLTEyMyJ9.aPA4Rlc4kw7n_OZZRRk25xZydJy_J_3BRR8ryYLyHTO1o68_aNWWQCgpnAuOW64svPhPnLYYnQzK-l2vHX34B64JySyBD4y_vRObGmdwH_SEufBAWZV7mkG3Y4mTKT3_4EWNu4VH92IhdnkGI4GJB6yHAEzlQI6EdSOa4Nq8Gp4uPGqHsUZTJrA3uIW0TbNshFBm47-oVM3ZUrBz57JKtr0e9jv0HjPQWyvbzx1HuxZd6eA8ow8xzvooKXFxoSFCMnxotd3wagvYQ9ysBa89bgzL-lhjWtusuMFDUVYwFqADE7oOSOD4Vtclgq8svznBQ-YpfTHfb9QEcofMlpyjNA" http://127.0.0.1:8080/secured/roles-allowed-admin; echo</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code data-lang="shell" class="language-shell hljs">$ curl -H "Authorization: Bearer eyJraWQ..." http://127.0.0.1:8080/secured/roles-allowed-admin; echo
hello + jdoe@quarkus.io, isHttps: false, authScheme: MP-JWT, hasJWT: true, birthdate: 2001-07-13</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="package-and-run-the-application"><a class="anchor" href="#package-and-run-the-application"></a>Package and run the application</h3>
<div class="paragraph">
<p>As usual, the application can be packaged using <code>./mvnw clean package</code> and executed using the <code>-runner.jar</code> file:
.Runner jar Example</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code data-lang="shell" class="language-shell hljs">Scotts-iMacPro:security-jwt-quickstart starksm$ ./mvnw clean package
[INFO] Scanning for projects...
...
[INFO] [io.quarkus.creator.phase.runnerjar.RunnerJarPhase] Building jar: /Users/starksm/Dev/JBoss/Protean/starksm64-quarkus-quickstarts/security-jwt-quickstart/target/security-jwt-quickstart-runner.jar

Scotts-iMacPro:security-jwt-quickstart starksm$ java -jar target/security-jwt-quickstart-runner.jar
2019-03-28 14:27:48,839 INFO  [io.quarkus] (main) Quarkus 0.12.0 started in 0.796s. Listening on: http://[::]:8080
2019-03-28 14:27:48,841 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy, resteasy-jsonb, security, smallrye-jwt]</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can also generate the native executable with <code>./mvnw clean package -Pnative</code>.
.Native Executable Example</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code data-lang="shell" class="language-shell hljs">Scotts-iMacPro:security-jwt-quickstart starksm$ ./mvnw clean package -Pnative
[INFO] Scanning for projects...
...
[security-jwt-quickstart-runner:25602]     universe:     493.17 ms
[security-jwt-quickstart-runner:25602]      (parse):     660.41 ms
[security-jwt-quickstart-runner:25602]     (inline):   1,431.10 ms
[security-jwt-quickstart-runner:25602]    (compile):   7,301.78 ms
[security-jwt-quickstart-runner:25602]      compile:  10,542.16 ms
[security-jwt-quickstart-runner:25602]        image:   2,797.62 ms
[security-jwt-quickstart-runner:25602]        write:     988.24 ms
[security-jwt-quickstart-runner:25602]      [total]:  43,778.16 ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  51.500 s
[INFO] Finished at: 2019-03-28T14:30:56-07:00
[INFO] ------------------------------------------------------------------------

Scotts-iMacPro:security-jwt-quickstart starksm$ ./target/security-jwt-quickstart-runner
2019-03-28 14:31:37,315 INFO  [io.quarkus] (main) Quarkus 0.12.0 started in 0.006s. Listening on: http://[::]:8080
2019-03-28 14:31:37,316 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy, resteasy-jsonb, security, smallrye-jwt]</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="explore-the-solution"><a class="anchor" href="#explore-the-solution"></a>Explore the Solution</h3>
<div class="paragraph">
<p>The solution repository located in the <code>security-jwt-quickstart</code> <a href="https://github.com/quarkusio/quarkus-quickstarts/tree/master/security-jwt-quickstart">directory</a> contains all of the versions we have
worked through in this quickstart guide as well as some additional endpoints that illustrate subresources with injection
of <code>JsonWebToken</code>s and their claims into those using the CDI APIs. We suggest that you check out the quickstart solutions and
explore the <code>security-jwt-quickstart</code> directory to learn more about the SmallRye JWT extension features.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="configuration-reference"><a class="anchor" href="#configuration-reference"></a>Configuration Reference</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="quarkus-configuration"><a class="anchor" href="#quarkus-configuration"></a>Quarkus configuration</h3>
<div class="paragraph configuration-legend">
<p><span class="icon"><i class="fa fa-lock" title="Fixed at build time"></i></span> Configuration property fixed at build time - All other configuration properties are overridable at runtime</p>
</div>
<table class="tableblock frame-all grid-all stretch configuration-reference searchable">
<colgroup>
<col style="width: 80%;">
<col style="width: 10%;">
<col style="width: 10%;">
</colgroup>
<tbody>
<tr>
<th class="tableblock halign-left valign-top"><p class="tableblock"><a id="quarkus-smallrye-jwt_configuration"></a><a href="#quarkus-smallrye-jwt_configuration">Configuration property</a></p></th>
<th class="tableblock halign-left valign-middle"><p class="tableblock">Type</p></th>
<th class="tableblock halign-left valign-middle"><p class="tableblock">Default</p></th>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph">
<p><span class="icon"><i class="fa fa-lock" title="Fixed at build time"></i></span> <a id="quarkus-smallrye-jwt_quarkus.smallrye-jwt.enabled"></a><code><a href="#quarkus-smallrye-jwt_quarkus.smallrye-jwt.enabled">quarkus.smallrye-jwt.enabled</a></code></p>
</div>
<div class="openblock description">
<div class="content">
<div class="paragraph">
<p>The MP-JWT configuration object</p>
</div>
</div>
</div></div></td>
<td class="tableblock halign-left valign-middle"><p class="tableblock">boolean</p></td>
<td class="tableblock halign-left valign-middle"><p class="tableblock"><code>true</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph">
<p><span class="icon"><i class="fa fa-lock" title="Fixed at build time"></i></span> <a id="quarkus-smallrye-jwt_quarkus.smallrye-jwt.rsa-sig-provider"></a><code><a href="#quarkus-smallrye-jwt_quarkus.smallrye-jwt.rsa-sig-provider">quarkus.smallrye-jwt.rsa-sig-provider</a></code></p>
</div>
<div class="openblock description">
<div class="content">
<div class="paragraph">
<p>The name of the <code>java.security.Provider</code> that supports SHA256withRSA signatures</p>
</div>
</div>
</div></div></td>
<td class="tableblock halign-left valign-middle"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-middle"><p class="tableblock"><code>SunRsaSign</code></p></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="microprofile-jwt-configuration"><a class="anchor" href="#microprofile-jwt-configuration"></a>MicroProfile JWT configuration</h3>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 50%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Property Name</th>
<th class="tableblock halign-left valign-top">Default</th>
<th class="tableblock halign-left valign-top">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>mp.jwt.verify.publickey</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>none</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The <code>mp.jwt.verify.publickey</code> config property allows the Public Key text itself to be supplied as a string.  The Public Key will be parsed from the supplied string in the order defined in section <a href="#supported-public-key-formats">Supported Public Key Formats</a>.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>mp.jwt.verify.publickey.location</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>none</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Config property allows for an external or internal location of Public Key to be specified.  The value may be a relative path or a URL. If the value points to an HTTPS based JWK set then, for it to work in native mode, the <code>quarkus.ssl.native</code> property must also be set to <code>true</code>, see <a href="native-and-ssl">Using SSL With Native Executables</a> for more details.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>mp.jwt.verify.issuer</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>none</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Config property specifies the value of the <code>iss</code> (issuer)
                           claim of the JWT that the server will accept as valid.</p></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="supported-public-key-formats"><a class="anchor" href="#supported-public-key-formats"></a>Supported Public Key Formats</h3>
<div class="paragraph">
<p>Public Keys may be formatted in any of the following formats, specified in order of
precedence:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Public Key Cryptography Standards #8 (PKCS#8) PEM</p>
</li>
<li>
<p>JSON Web Key (JWK)</p>
</li>
<li>
<p>JSON Web Key Set (JWKS)</p>
</li>
<li>
<p>JSON Web Key (JWK) Base64 URL encoded</p>
</li>
<li>
<p>JSON Web Key Set (JWKS) Base64 URL encoded</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="additional-smallrye-jwt-configuration"><a class="anchor" href="#additional-smallrye-jwt-configuration"></a>Additional SmallRye JWT configuration</h3>
<div class="paragraph">
<p>SmallRye JWT provides more properties which can be used to customize the token processing:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 50%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Property Name</th>
<th class="tableblock halign-left valign-top">Default</th>
<th class="tableblock halign-left valign-top">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.verify.algorithm</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code><code>RS256</code></code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Signature algorithm. Set it to <code>ES256</code> to support the Elliptic Curve signature algorithm.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.verify.key-format</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code><code>ANY</code></code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Set this property to a specific key format such as <code>PEM_KEY</code>, <code>PEM_CERTIFICATE</code>, <code>JWK</code> or <code>JWK_BASE64URL</code> to optimize the way the verification key is loaded.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.verify.relax-key-validation</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Relax the validation of the verification keys, setting this property to <code>true</code> will allow public RSA keys with the length less than 2048 bit.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.token.header</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code><code>Authorization</code></code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Set this property if another header such as <code>Cookie</code> is used to pass the token.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.token.cookie</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>none</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Name of the cookie containing a token. This property will be effective only if  <code>smallrye.jwt.token.header</code> is set to <code>Cookie</code>.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.always-check-authorization</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Set this property to true for Authorization header be checked even if the smallrye.jwt.token.header is set to Cookie but no cookie with a smallrye.jwt.token.cookie name exists.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.token.schemes</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code><code>Bearer</code></code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Comma-separated list containing an alternative single or multiple schemes, for example, <code>DPoP</code>.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.token.kid</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>none</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Key identifier. If it is set then the verification JWK key as well every JWT token must have a matching <code>kid</code> header.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.time-to-live</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>none</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The maximum number of seconds that a JWT may be issued for use. Effectively, the difference between the expiration date of the JWT and the issued at date must not exceed this value.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.require.named-principal</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code><code>false</code></code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">If an application relies on <code>java.security.Principal</code> returning a name then a token must have a <code>upn</code> or <code>preferred_username</code> or <code>sub</code> claim set. Setting this property will result in SmallRye JWT throwing an exception if none of these claims is available for the application code to reliably deal with a non-null <code>Principal</code> name.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.path.sub</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>none</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Path to the claim containing the subject name. It starts from the top level JSON object and can contain multiple segments where each segment represents a JSON object name only, example: <code>realms/subject</code>. This property can be used if a token has no 'sub' claim but has the subject set in a different claim. Use double quotes with the namespace qualified claims.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.claims.sub</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>none</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">This property can be used to set a default sub claim value when the current token has no standard or custom <code>sub</code> claim available. Effectively this property can be used to customize <code>java.security.Principal</code> name if no <code>upn</code> or <code>preferred_username</code> or <code>sub</code> claim is set.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.path.groups</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>none</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Path to the claim containing the groups. It starts from the top level JSON object and can contain multiple segments where each segment represents a JSON object name only, example: <code>realm/groups</code>. This property can be used if a token has no 'groups' claim but has the groups set in a different claim. Use double quotes with the namespace qualified claims.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.groups-separator</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>' '</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Separator for splitting a string which may contain multiple group values. It will only be used if the <code>smallrye.jwt.path.groups</code> property points to a custom claim whose value is a string. The default value is a single space because a standard OAuth2 <code>scope</code> claim may contain a space separated sequence.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.claims.groups</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>none</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">This property can be used to set a default groups claim value when the current token has no standard or custom groups claim available.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.jwks.refresh-interval</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>60</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">JWK cache refresh interval in minutes. It will be ignored unless the <code>mp.jwt.verify.publickey.location</code> points to the HTTPS URL based JWK set and no HTTP <code>Cache-Control</code> response header with a positive <code>max-age</code> parameter value is returned from a JWK HTTPS endpoint.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.jwks.forced-refresh-interval</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>30</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Forced JWK cache refresh interval in minutes which is used to restrict the frequency of the forced refresh attempts which may happen when the token verification fails due to the cache having no JWK key with a kid property matching the current token&#8217;s kid header. It will be ignored unless the mp.jwt.verify.publickey.location points to the HTTPS URL based JWK set.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.expiration.grace</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>60</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Expiration grace in seconds. By default an expired token will still be accepted if the current time is no more than 1 min after the token expiry time.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.verify.aud</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>none</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Comma separated list of the audiences that a token <code>aud</code> claim may contain.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.required.claims</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>none</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Comma separated list of the claims that a token must contain.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.decrypt.key-location</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>none</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Config property allows for an external or internal location of Private Decryption Key to be specified.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.decrypt.algorithm</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code><code>RSA_OAEP</code></code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Decryption algorithm.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.token.decryption.kid</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>none</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Decryption Key identifier. If it is set then the decryption JWK key as well every JWT token must have a matching <code>kid</code> header.</p></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="create-jsonwebtoken-with-jwtparser"><a class="anchor" href="#create-jsonwebtoken-with-jwtparser"></a>Create JsonWebToken with JWTParser</h2>
<div class="sectionbody">
<div class="paragraph">
<p>If the JWT token can not be injected, for example, if it is embedded in the service request payload or the service endpoint acquires it out of band, then one can use <code>JWTParser</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-none hljs">import org.eclipse.microprofile.jwt.JsonWebToken;
import io.smallrye.jwt.auth.principal.JWTParser;
...
@Inject JWTParser parser;

String token = getTokenFromOidcServer();

// Parse and verify the token
JsonWebToken jwt = parser.parse(token);</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can also use it to customize the way the token is verified or decrypted. For example, one can supply a local <code>SecretKey</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-none hljs">import javax.crypto.SecretKey;
import javax.ws.rs.GET;
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.core.Response;
import org.eclipse.microprofile.jwt.JsonWebToken;
import io.smallrye.jwt.auth.principal.JWTParser;
import io.smallrye.jwt.build.Jwt;

@Path("/secured")
public class SecuredResource {
 @Inject JWTParser parser;
 private SecretKey key = createSecretKey();

 @GET
 @Produces("text/plain")
 public Response getUserName(@CookieParam("jwt") String jwtCookie) {
    Response response = null;
    if (jwtCookie == null) {
        String newJwtCookie = Jwt.upn("Alice").sign(key);
        // or newJwtCookie = Jwt.upn("alice").encrypt(key);
        return Response.ok("Alice").cookie(new NewCookie("jwt", newJwtCookie)).build();
    else {
        // All mp.jwt and smallrye.jwt properties are still effective, only the verification key is customized.
        JsonWebToken jwt = parser.verify(jwtCookie, key);
        // or jwt = parser.decrypt(jwtCookie, key);
        return Response.ok(jwt.getName()).build();
    }
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="token-decryption"><a class="anchor" href="#token-decryption"></a>Token Decryption</h2>
<div class="sectionbody">
<div class="paragraph">
<p>If your application needs to accept the tokens with the encrypted claims or with the encrypted inner signed claims then all you have to do is to set
<code>smallrye.jwt.decrypt.key-location</code> pointing to the decryption key.</p>
</div>
<div class="paragraph">
<p>If this is the only key property which is set then the incoming token is expected to contain the encrypted claims only.
If either <code>mp.jwt.verify.publickey</code> or <code>mp.jwt.verify.publickey.location</code> verification properties are also set then the incoming token is expected to contain
the encrypted inner-signed token.</p>
</div>
<div class="paragraph">
<p>See <a href="#generate-jwt-tokens">Generate JWT tokens with SmallRye JWT</a> and learn how to generate the encrypted or inner-signed and then encrypted tokens fast.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="how-to-check-the-errors-in-the-logs"><a class="anchor" href="#how-to-check-the-errors-in-the-logs"></a>How to check the errors in the logs</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Set <code>quarkus.log.category."io.quarkus.smallrye.jwt.runtime.auth.MpJwtValidator".level=TRACE</code> to see more details about the token verification or decryption errors.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="generate-jwt-tokens"><a class="anchor" href="#generate-jwt-tokens"></a>Generate JWT tokens with SmallRye JWT</h2>
<div class="sectionbody">
<div class="paragraph">
<p>JWT claims can be signed or encrypted or signed first and the nested JWT token encrypted.
Signing the claims is used most often to secure the claims. What is known today as a JWT token is typically produced by signing the claims in a JSON format using the steps described in the <a href="https://tools.ietf.org/html/rfc7515">JSON Web Signature</a> specification.
However, when the claims are sensitive, their confidentiality can be guaranteed by following the steps described in the <a href="https://tools.ietf.org/html/rfc7516">JSON Web Encryption</a> specification to produce a JWT token with the encrypted claims.
Finally both the confidentiality and integrity of the claims can be further enforced by signing them first and then encrypting the nested JWT token.</p>
</div>
<div class="paragraph">
<p>SmallRye JWT provides an API for securing the JWT claims using all of these options.</p>
</div>
<div class="sect2">
<h3 id="create-jwtclaimsbuilder-and-set-the-claims"><a class="anchor" href="#create-jwtclaimsbuilder-and-set-the-claims"></a>Create JwtClaimsBuilder and set the claims</h3>
<div class="paragraph">
<p>The first step is to initialize a <code>JwtClaimsBuilder</code> using one of the options below and add some claims to it:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code data-lang="java" class="language-java hljs">import java.util.Collections;
import javax.json.Json;
import javax.json.JsonObject;
import io.smallrye.jwt.build.Jwt;
import io.smallrye.jwt.build.JwtClaimsBuilder;
import org.eclipse.microprofile.jwt.JsonWebToken;
...
// Create an empty builder and add some claims
JwtClaimsBuilder builder1 = Jwt.claims();
builder1.claim("customClaim", "custom-value").issuer("https://issuer.org");
// Or start typing the claims immediately:
// JwtClaimsBuilder builder1 = Jwt.upn("Alice");

// Builder created from the existing claims
JwtClaimsBuilder builder2 = Jwt.claims("/tokenClaims.json");

// Builder created from a map of claims
JwtClaimsBuilder builder3 = Jwt.claims(Collections.singletonMap("customClaim", "custom-value"));

// Builder created from JsonObject
JsonObject userName = Json.createObjectBuilder().add("username", "Alice").build();
JsonObject userAddress = Json.createObjectBuilder().add("city", "someCity").add("street", "someStreet").build();
JsonObject json = Json.createObjectBuilder(userName).add("address", userAddress).build();
JwtClaimsBuilder builder4 = Jwt.claims(json);

// Builder created from JsonWebToken
@Inject JsonWebToken token;
JwtClaimsBuilder builder5 = Jwt.claims(token);</code></pre>
</div>
</div>
<div class="paragraph">
<p>The API is fluent so the builder initialization can be done as part of the fluent API sequence.</p>
</div>
<div class="paragraph">
<p>The builder will also set <code>iat</code> (issued at) to the current time, <code>exp</code> (expires at) to 5 minutes away from the current time (it can be customized with the <code>smallrye.jwt.new-token.lifespan</code> property) and <code>jti</code> (unique token identifier) claims if they have not already been set.
One can also configure <code>smallrye.jwt.new-token.issuer</code> property and skip setting the issuer directly with the builder API.</p>
</div>
<div class="paragraph">
<p>The next step is to decide how to secure the claims.</p>
</div>
</div>
<div class="sect2">
<h3 id="sign-the-claims"><a class="anchor" href="#sign-the-claims"></a>Sign the claims</h3>
<div class="paragraph">
<p>The claims can be signed immediately or after the <code>JSON Web Signature</code> headers have been set:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code data-lang="java" class="language-java hljs">import io.smallrye.jwt.build.Jwt;
...

// Sign the claims using the private key loaded from the location set with a 'smallrye.jwt.sign.key-location' property.
// No 'jws()' transition is necessary.
String jwt1 = Jwt.claims("/tokenClaims.json").sign();

// Set the headers and sign the claims with an RSA private key loaded in the code (the implementation of this method is omitted). Note a 'jws()' transition to a 'JwtSignatureBuilder'.
String jwt2 = Jwt.claims("/tokenClaims.json").jws().keyId("kid1").header("custom-header", "custom-value").sign(getPrivateKey());</code></pre>
</div>
</div>
<div class="paragraph">
<p>Note the <code>alg</code> (algorithm) header is set to <code>RS256</code> by default.</p>
</div>
</div>
<div class="sect2">
<h3 id="encrypt-the-claims"><a class="anchor" href="#encrypt-the-claims"></a>Encrypt the claims</h3>
<div class="paragraph">
<p>The claims can be encrypted immediately or after the <code>JSON Web Encryption</code> headers have been set the same way as they can be signed.
The only minor difference is that encrypting the claims always requires a <code>jwe()</code> <code>JwtEncryptionBuilder</code> transition:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code data-lang="java" class="language-java hljs">import io.smallrye.jwt.build.Jwt;
...

// Encrypt the claims using the public key loaded from the location set with a 'smallrye.jwt.encrypt.key-location' property.
String jwt1 = Jwt.claims("/tokenClaims.json").jwe().encrypt();

// Set the headers and encrypt the claims with an RSA public key loaded in the code (the implementation of this method is omitted).
String jwt2 = Jwt.claims("/tokenClaims.json").jwe().header("custom-header", "custom-value").encrypt(getPublicKey());</code></pre>
</div>
</div>
<div class="paragraph">
<p>Note the <code>alg</code> (key management algorithm) header is set to <code>RSA-OAEP-256</code> (it will be changed to <code>RSA-OAEP</code> in a future version of smallrye-jwt) and the <code>enc</code> (content encryption header) is set to <code>A256GCM</code> by default.</p>
</div>
</div>
<div class="sect2">
<h3 id="sign-the-claims-and-encrypt-the-nested-jwt-token"><a class="anchor" href="#sign-the-claims-and-encrypt-the-nested-jwt-token"></a>Sign the claims and encrypt the nested JWT token</h3>
<div class="paragraph">
<p>The claims can be signed and then the nested JWT token encrypted by combining the sign and encrypt steps.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code data-lang="java" class="language-java hljs">import io.smallrye.jwt.build.Jwt;
...

// Sign the claims and encrypt the nested token using the private and public keys loaded from the locations set with the 'smallrye.jwt.sign.key-location' and 'smallrye.jwt.encrypt.key-location' properties respectively.
String jwt = Jwt.claims("/tokenClaims.json").innerSign().encrypt();</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="fast-jwt-generation"><a class="anchor" href="#fast-jwt-generation"></a>Fast JWT Generation</h3>
<div class="paragraph">
<p>If <code>smallrye.jwt.sign.key-location</code> or/and <code>smallrye.jwt.encrypt.key-location</code> properties are set then one can secure the existing claims (resources, maps, JsonObjects) with a single call:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-none hljs">// More compact than Jwt.claims("/claims.json").sign();
Jwt.sign("/claims.json");

// More compact than Jwt.claims("/claims.json").jwe().encrypt();
Jwt.encrypt("/claims.json");

// More compact than Jwt.claims("/claims.json").innerSign().encrypt();
Jwt.signAndEncrypt("/claims.json");</code></pre>
</div>
</div>
<div class="paragraph">
<p>As mentioned above, <code>iat</code>, <code>exp</code>, <code>jti</code> and <code>iss</code> claims will be added if needed.</p>
</div>
</div>
<div class="sect2">
<h3 id="smallrye-jwt-builder-configuration"><a class="anchor" href="#smallrye-jwt-builder-configuration"></a>SmallRye JWT Builder configuration</h3>
<div class="paragraph">
<p>Smallrye JWT supports the following properties which can be used to customize the way claims are signed and encrypted:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 50%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Property Name</th>
<th class="tableblock halign-left valign-top">Default</th>
<th class="tableblock halign-left valign-top">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.sign.key-location</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code><code>none</code></code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Location of a private key which will be used to sign the claims when either a no-argument <code>sign()</code> or <code>innerSign()</code> method is called.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.encrypt.key-location</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code><code>none</code></code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Location of a public key which will be used to encrypt the claims or inner JWT when a no-argument <code>encrypt()</code> method is called.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.new-token.lifespan</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>300</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Token lifespan in seconds which will be used to calculate an exp (expiry) claim value if this claim has not already been set.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>smallrye.jwt.new-token.issuer</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>none</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Token issuer which can be used to set an iss (issuer) claim value if this claim has not already been set.</p></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="references"><a class="anchor" href="#references"></a>References</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p><a href="https://github.com/eclipse/microprofile-jwt-auth/releases/download/1.1.1/microprofile-jwt-auth-spec.html">MP JWT 1.1.1 HTML</a></p>
</li>
<li>
<p><a href="https://github.com/eclipse/microprofile-jwt-auth/releases/download/1.1.1/microprofile-jwt-auth-spec.pdf">MP JWT 1.1.1 PDF</a></p>
</li>
<li>
<p><a href="https://github.com/smallrye/smallrye-jwt">SmallRye JWT</a></p>
</li>
<li>
<p><a href="https://tools.ietf.org/html/rfc7519">JSON Web Token</a></p>
</li>
<li>
<p><a href="https://tools.ietf.org/html/rfc7515">JSON Web Signature</a></p>
</li>
<li>
<p><a href="https://tools.ietf.org/html/rfc7516">JSON Web Encryption</a></p>
</li>
<li>
<p><a href="https://tools.ietf.org/html/rfc7518">JSON Web Algorithms</a></p>
</li>
</ul>
</div>
</div>
</div>
    </div>
  </div>
</div>

  </div>

  <div class="content project-footer">
  <div class="footer-section">
    <div class="logo-wrapper">
      <a href="/"><img src="/assets/images/quarkus_logo_horizontal_rgb_reverse.svg" class="project-logo" title="Quarkus"></a>
    </div>
  </div>
  <div class="grid-wrapper">
    <p class="grid__item width-3-12">Quarkus is open. All dependencies of this project are available under the <a href='https://www.apache.org/licenses/LICENSE-2.0' target='_blank'>Apache Software License 2.0</a> or compatible license.<br /><br />This website was built with <a href='https://jekyllrb.com/' target='_blank'>Jekyll</a>, is hosted on <a href='https://pages.github.com/' target='_blank'>Github Pages</a> and is completely open source. If you want to make it better, <a href='https://github.com/quarkusio/quarkusio.github.io' target='_blank'>fork the website</a> and show us what you’ve got.</p>

    
      <div class="width-1-12 project-links">
        <span>Navigation</span>
        <ul class="footer-links width-1-12">
          
            <li><a href="/">Home</a></li>
          
            <li><a href="/guides">Guides</a></li>
          
            <li><a href="/community/#contributing">Contribute</a></li>
          
            <li><a href="/faq">FAQ</a></li>
          
            <li><a href="/get-started">Get Started</a></li>
          
        </ul>
      </div>
    
      <div class="width-1-12 project-links">
        <span>Contribute</span>
        <ul class="footer-links width-1-12">
          
            <li><a href="https://twitter.com/quarkusio">Follow us</a></li>
          
            <li><a href="https://github.com/quarkusio">GitHub</a></li>
          
            <li><a href="/security">Security&nbsp;policy</a></li>
          
        </ul>
      </div>
    
      <div class="width-1-12 project-links">
        <span>Get Help</span>
        <ul class="footer-links width-1-12">
          
            <li><a href="https://groups.google.com/forum/#!forum/quarkus-dev">Forums</a></li>
          
            <li><a href="https://quarkusio.zulipchat.com">Chatroom</a></li>
          
        </ul>
      </div>
    

    
      <div class="width-3-12 more-links">
        <span>Quarkus is made of community projects</span>
        <ul class="footer-links">
          
            <li><a href="https://vertx.io/" target="_blank">Eclipse Vert.x</a></li>
          
            <li><a href="https://microprofile.io" target="_blank">Eclipse MicroProfile</a></li>
          
            <li><a href="https://hibernate.org" target="_blank">Hibernate</a></li>
          
            <li><a href="https://netty.io" target="_blank">Netty</a></li>
          
            <li><a href="https://resteasy.github.io" target="_blank">RESTEasy</a></li>
          
            <li><a href="https://camel.apache.org" target="_blank">Apache Camel</a></li>
          
            <li><a href="https://code.quarkus.io/" target="_blank">And many more...</a></li>
          
        </ul>
      </div>
    
  </div>
</div>
  <div class="content redhat-footer">
  <div class="grid-wrapper">
    <span class="licence">
      <i class="fab fa-creative-commons"></i><i class="fab fa-creative-commons-by"></i> <a href="https://creativecommons.org/licenses/by/3.0/" target="_blank">CC by 3.0</a> | <a href="https://www.redhat.com/en/about/privacy-policy">Privacy Policy</a>
    </span>
    <span class="redhat">
      Sponsored by
    </span>
    <span class="redhat-logo">
      <a href="https://www.redhat.com/" target="_blank"><img src="/assets/images/redhat_reversed.svg"></a>
    </span>
  </div>
</div>


  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js" integrity="sha384-8gBf6Y4YYq7Jx97PIqmTwLPin4hxIzQw5aDmUg/DDhul9fFpbbLcLh3nTIIDJKhx" crossorigin="anonymous"></script>
  <script type="text/javascript" src="/assets/javascript/mobile-nav.js"></script>
  <script type="text/javascript" src="/assets/javascript/scroll-down.js"></script>
  <script src="/assets/javascript/satellite.js" type="text/javascript"></script>
  <script src="https://quarkus.io/guides/javascript/config.js" type="text/javascript"></script>
  <script src="/assets/javascript/search-filter.js" type="text/javascript"></script>
  <script src="/assets/javascript/back-to-top.js" type="text/javascript"></script>
</body>

</html>
